package service

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"gitee.com/zh-jn/harmonica-previewer-online/api"
	storage "gitee.com/zh-jn/harmonica-previewer-online/storage/localstorage"
)

type Config struct {
	Addr    string
	Port    int
	BaseDir string
}

var s api.Storage

func RunServer(cfg Config) {
	var err error
	if s, err = storage.NewLocalFileStorage(cfg.BaseDir); err != nil {
		panic(err)
	}

	if err = http.ListenAndServe(fmt.Sprintf("%v:%v", cfg.Addr, cfg.Port), http.HandlerFunc(handler)); err != nil {
		panic(err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	if strings.HasPrefix(path, "/get/") {
		get(w, r)
		return
	}

	if strings.HasPrefix(path, "/put/") {
		put(w, r)
		return
	}

	if strings.HasPrefix(path, "/exists/") {
		exists(w, r)
		return
	}
}

func get(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	key := strings.TrimPrefix(path, "/get/")
	if key == "" {
		w.WriteHeader(http.StatusOK)
		w.Write(rsp(-2, "", "invalid path: missing id"))
		return
	}

	reader, err := s.Get(api.StorageKey(key))
	if err != nil {
		w.WriteHeader(http.StatusOK)
		w.Write(rsp(-3, "", "read from local storage err"))
		return
	}

	buf, err := ioutil.ReadAll(reader)
	if err != nil {
		w.WriteHeader(http.StatusOK)
		w.Write(rsp(-4, "", "read content err"))
		return
	}
	data := base64.StdEncoding.EncodeToString(buf)
	w.WriteHeader(http.StatusOK)
	w.Write(rsp(0, data, ""))
	return
}

func put(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	key := strings.TrimPrefix(path, "/put/")
	if key == "" {
		w.WriteHeader(http.StatusOK)
		w.Write(rsp(-2, "", "invalid path: missing id"))
		return
	}

	if err := s.Put(api.StorageKey(key), r.Body); err != nil {
		w.WriteHeader(http.StatusOK)
		w.Write(rsp(-3, "", "store body err"))
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(rsp(0, "", ""))
	return
}

func exists(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	key := strings.TrimPrefix(path, "/exists/")
	if key == "" {
		w.WriteHeader(http.StatusOK)
		w.Write(api.RspBuf(nil, fmt.Errorf("invalid path: missing id")))
		return
	}

	ok, err := s.Exists(api.StorageKey(key))
	if err != nil {
		w.WriteHeader(http.StatusOK)
		w.Write(api.RspBuf(nil, fmt.Errorf("store body err, %v", err)))
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(api.RspBuf(ok, nil))
	return
}

func rsp(code int, data, msg string) []byte {
	r := struct {
		Code int    `json:"code"`
		Data string `json:"data"`
		Msg  string `json:"msg"`
	}{
		Code: code,
		Data: data,
		Msg:  msg,
	}
	buf, err := json.Marshal(r)
	if err != nil {
		return []byte(`{"code":-1,"msg":"inner err: marshal json err"}`)
	}
	return buf
}
